import {
    _decorator,
    Color,
    Component,
    director,
    instantiate,
    Node,
    Prefab,
    ScrollView,
    Size,
    Sprite,
    TiledLayer,
    TiledMap,
    UITransform,
    Vec2,
    Vec3,
} from "cc";
import {NormalBuilding} from "./normal-building";
import {GameEvent} from "db://assets/script/game-event";
import {CityBuildingTryPut} from "db://assets/script/common/events/city-events/city-building-try-put";
import {CityBuildingPut} from "db://assets/script/common/events/city-events/city-building-put";
import {lobby} from "db://assets/script/lobby";
import {CityBuildingMove} from "db://assets/script/common/events/city-events/city-building-move";
import {userInfo} from "db://assets/script/user-info";
import {CityBuildingRemove} from "db://assets/script/common/events/city-events/city-building-remove";
import {CityBuildingBeginEdit} from "db://assets/script/common/events/city-events/city-building-begin-edit";
import {CityBuildingPrePut} from "db://assets/script/common/events/city-events/city-building-pre-put";
import {NormalBuildingStatus} from "db://assets/script/game/constants/normal-building-status";
import Long from "long";
import {BuildingItem} from "db://assets/proto/new-world-api/v1/building";
import {CityBuildingMovePut} from "db://assets/script/common/events/city-events/city-building-move-put";
import {CityBuildingUpgrade} from "db://assets/script/common/events/city-events/city-building-upgrade";
import {cityUtils} from "db://assets/script/utils/city-utils";
import {configsHelper} from "db://assets/script/configs-helper";
import {CityBuildingCancelEdit} from "db://assets/script/common/events/city-events/city-building-cancel-edit";
import calcCellId = cityUtils.calcCellId;
import calcTilePositionByCellId = cityUtils.calcTilePositionByCellId;
import {cmm} from "db://assets/script/utils/cmm";
import {SortedMapByValue} from "db://assets/script/pkg/sorted-map-by-value";
import {CityBuildingCheckRemove} from "db://assets/script/common/events/city-events/city-building-check-remove";

const {ccclass, property} = _decorator;

class Shadow {
    node: Node
    cellId: bigint

    constructor(node: Node, cellId: bigint) {
        this.node = node;
        this.cellId = cellId;
    }
}

@ccclass('CityField')
export class CityField extends Component {

    @property(TiledMap)
    tiledMap!: TiledMap


    @property(Node)
    buildings!: Node

    @property(Node)
    buildingShadowsBox!: Node

    @property(Prefab)
    buildingAsset!: Prefab

    @property(ScrollView)
    scrollView!: ScrollView

    @property(Prefab)
    buildingShadowPref!: Prefab

    @property(TiledLayer)
    baseLayer!: TiledLayer

    @property(Node)
    cityBase!: Node

    occupiedCellIds: Map<bigint, Vec2> = new Map()

    buildingCaches: Map<bigint, NormalBuilding> = new Map()

    shadowPool: Shadow[] = []

    buildingSibling: SortedMapByValue<bigint, number> = new SortedMapByValue()
    airBuid: Long | null = null

    // buildingSibling: OrderedSet<bigint> = new OrderedSet()

    start() {
        this.onEvent()
        const buildings = userInfo.cityInfo.loadBuildings(1)
        const bm = cmm.array2Map(buildings, (item) => {
            return {
                k: calcCellId(item.x, item.y),
                v: item
            }
        })
        //@ts-ignore
        this.scrollView._stopPropagationIfTargetIsMe = function (event: EventTouch) {
            event.propagationStopped = false;
        }
        const pss = cityUtils.rowByRowDiagonalTraverse(50, 50)
        let tmp: bigint = 0n
        for (let ps of pss) {
            const cellId = calcCellId(ps.x, ps.y)
            if (bm.has(cellId)) {
                const building = bm.get(cellId)!
                if (building.buildingId.eq(104)) {
                    tmp = cellId
                }
                const idx = cityUtils.universalDiagonalIndex(ps.x, ps.y, 50, 50)
                this.buildingSibling.set(cellId, idx)
                this._initShadow(building.x, building.y, building.buildingId)
                this._directPutBuilding(cellId, building)
            }
            // idx++
        }
        console.log('target building idx:', this.buildingSibling.getRankByKey(tmp))

        this._scrollMoveTiled(25, 23)
    }

    onEvent() {
        director.on(GameEvent.cityBuildingTryPut, this._tryPutCity, this)
        director.on(GameEvent.cityBuildingBeginEdit, this._beginEdit, this)
        director.on(GameEvent.cityBuildingPut, this._put, this)
        director.on(GameEvent.cityBuildingPrePut, this._prePut, this)
        director.on(GameEvent.cityBuildingPreMovePut, this._prePut, this)

        director.on(GameEvent.cityBuildingMove, this._changeTileStatusWhenBuildingMove, this)
        director.on(GameEvent.cityBuildingMovePut, this._movePut, this)
        director.on(GameEvent.cityBuildingCancelEdit, this._cancelCheck, this)

        director.on(GameEvent.cityBuildingRemove, this._removeBuilding, this)
        director.on(GameEvent.cityBuildingUpgrade, this._upgradeBuilding, this)
        director.on(GameEvent.cityBuildingCheckRemove, this._checkRemove, this)
    }

    offEvent() {
        director.off(GameEvent.cityBuildingTryPut, this._tryPutCity, this)
        director.off(GameEvent.cityBuildingBeginEdit, this._beginEdit, this)
        director.off(GameEvent.cityBuildingPut, this._put, this)
        director.off(GameEvent.cityBuildingPrePut, this._prePut, this)
        director.off(GameEvent.cityBuildingPreMovePut, this._prePut, this)

        director.off(GameEvent.cityBuildingMove, this._changeTileStatusWhenBuildingMove, this)
        director.off(GameEvent.cityBuildingMovePut, this._movePut, this)
        director.off(GameEvent.cityBuildingCancelEdit, this._cancelCheck, this)

        director.off(GameEvent.cityBuildingRemove, this._removeBuilding, this)
        director.off(GameEvent.cityBuildingUpgrade, this._upgradeBuilding, this)
        director.off(GameEvent.cityBuildingCheckRemove, this._checkRemove, this)
    }

    _tryPutCity(event: CityBuildingTryPut) {
        if (this.shadowPool.length > 0) {
            lobby.instance.showToast("请先处理上个建筑的编辑")
        } else {
            const userLevel = userInfo.loadUserLevel()
            const numLimit = configsHelper.loadUserLevelBuildingNumLimit(userLevel, event.buildingId)!
            const currentNum = userInfo.cityInfo.loadBuildingsNum(1, event.buildingId)
            if (currentNum >= numLimit) {
                lobby.instance.showToast('建筑数量达到上限')
                return
            }
            const ps = this.loadUnOccupiedCell()
            console.log('will put on', ps)
            const building = this._initBuilding(event.buildingId, null)
            this.buildings.addChild(building.node)
            building.changeStatus(NormalBuildingStatus.edit)
            building.moveToTiled(ps.x, ps.y, true)
            this._initShadow(ps.x, ps.y, event.buildingId)
            this._scrollMoveTiled(ps.x, ps.y)
            building.node.name = 'normal-building-' + event.buildingId
            console.log('try put', building)
        }
    }

    _beginEdit(event: CityBuildingBeginEdit) {
        const building = event.building
        if (this.shadowPool.length > 0) {
            lobby.instance.showToast("请先处理上个建筑的编辑")
        } else {
            const cellIds = cityUtils.calcBuildingOccupiedCellIds(event.building.tiledX, event.building.tiledY, event.building.buildingId)
            cellIds.forEach(cid => this.occupiedCellIds.delete(cid))

            this.buildingSibling.delete(event.building.cellId())
            this._initShadow(building.tiledX, building.tiledY, building.buildingId)
            building.node.setSiblingIndex(Number.MAX_SAFE_INTEGER)
            building.changeStatus(NormalBuildingStatus.edit)
            this.airBuid = building.buildingItem!.buildingUniqueId
        }
    }

    _prePut(event: CityBuildingPrePut) {
        const cellId = event.building.cellId()
        if (event.building.buildingItem) {
            const oldCellId = calcCellId(event.building.buildingItem.x, event.building.buildingItem.y)
            this.buildingCaches.delete(oldCellId)
        }
        if (!this.buildingCaches.has(cellId)) {
            this.buildingCaches.set(cellId, event.building)
        }
    }

    _put(event: CityBuildingPut) {
        const buildingItem = event.buildingItem
        const cellId = calcCellId(buildingItem.x, buildingItem.y)
        this._clearBuildingShadow(true)
        const building = this.buildingCaches.get(cellId)!
        const idx = cityUtils.universalDiagonalIndex(buildingItem.x, buildingItem.y, 50, 50)
        this.buildingSibling.set(cellId, idx)
        building.buildingItem = buildingItem
        building.beginUpgrade()
        this._sortBuilding(cellId, building)
    }

    _movePut(event: CityBuildingMovePut) {
        const cellId = calcCellId(event.buildingItem.x, event.buildingItem.y)
        this._clearBuildingShadow(true)
        const building = this.buildingCaches.get(cellId)!
        const idx = cityUtils.universalDiagonalIndex(event.buildingItem.x, event.buildingItem.y, 50, 50)
        this.buildingSibling.set(cellId, idx)
        building.changeStatus(NormalBuildingStatus.normal)
        this._sortBuilding(cellId, building)
    }

    _directPutBuilding(cellId: bigint, buildingItem: BuildingItem) {
        console.log('try direct put building', cellId, buildingItem.buildingId)
        if (this.occupiedCellIds.has(cellId)) {
            lobby.instance.showToast("该节点被占用, x" + buildingItem.x + 'y' + buildingItem.y)
        } else {
            this._clearBuildingShadow(true)
            const ps = calcTilePositionByCellId(cellId);
            const building = this._initBuilding(buildingItem.buildingId, buildingItem)
            this.buildings.addChild(building.node)
            building.changeStatus(NormalBuildingStatus.normal)
            building.moveToTiled(ps.x, ps.y, true)
            this.buildingCaches.set(cellId, building)
        }
    }

    _sortBuilding(cellId: bigint, building: NormalBuilding) {
        const sibling = this.buildingSibling.getRankByKey(cellId)
        console.log('new sibling', sibling)
        building.node.setSiblingIndex(sibling)
    }

    _clearBuildingShadow(put: boolean) {
        this.airBuid = null
        for (let shadow of this.shadowPool) {
            const vt = calcTilePositionByCellId(shadow.cellId)
            if (put) this.occupiedCellIds.set(shadow.cellId, vt)
            shadow.node.destroy()
        }
        this.shadowPool = []
    }

    _initBuilding(buildingId: Long, buildingItem: BuildingItem | null): NormalBuilding {
        const buildingNode = instantiate(this.buildingAsset)
        const building = buildingNode.getComponent(NormalBuilding)!
        buildingNode.name = 'normal-building-' + buildingId.toString() + '-' + buildingItem?.x.toString() + '-' + buildingItem?.y.toString()
        building.loadBuilding(buildingId, buildingItem, this.baseLayer)
        building.tileSize = this.tiledMap.getTileSize()
        building.inWhichScrollView = this.scrollView
        return building
    }

    _cancelCheck(event: CityBuildingCancelEdit) {
        this._clearBuildingShadow(!event.newBuilding)
        const idx = cityUtils.universalDiagonalIndex(event.building.tiledX, event.building.tiledY, 50, 50)
        this.buildingSibling.set(event.building.cellId(), idx)
        this._sortBuilding(event.building.cellId(), event.building)
    }

    _removeBuilding(event: CityBuildingRemove) {
        this._clearBuildingShadow(false)
        console.log('try begin remove', event.completeAtMs.toNumber())
        const cellId = calcCellId(event.x, event.y)
        const building = this.buildingCaches.get(cellId)
        if (building) building.beginRemove()
    }

    _checkRemove(event: CityBuildingCheckRemove) {
        if (this.airBuid?.eq(event.buildingItem!.buildingUniqueId)) {
            this._clearBuildingShadow(false)
        }
    }


    _upgradeBuilding(event: CityBuildingUpgrade) {
        this._clearBuildingShadow(true)
        const cellId = calcCellId(event.x, event.y)
        const building = this.buildingCaches.get(cellId)
        if (building) building.beginUpgrade()
    }

    loadUnOccupiedCell(): Vec2 {
        for (let x = 49; x >= 0; x--) {
            for (let y = 49; y >= 0; y--) {
                const cellId = calcCellId(x, y)
                if (userInfo.cityInfo.checkOneCellUnlockCell(cellId)) continue
                if (!this.occupiedCellIds.has(cellId)) {
                    return new Vec2(x, y)
                }
            }
        }
        return new Vec2(23, 15)
    }

    _changeTileStatusWhenBuildingMove(event: CityBuildingMove) {
        const newCellIds = cityUtils.calcBuildingOccupiedCellIds(event.toX, event.toY, event.buildingId)
        this._occupiedCell(newCellIds)
    }

    _initShadow(x: number, y: number, buildingId: Long) {
        const cellIds = cityUtils.calcBuildingOccupiedCellIds(x, y, buildingId)
        for (const cellId of cellIds) {
            const ps = calcTilePositionByCellId(cellId);
            const tile = this.baseLayer.getTiledTileAt(ps.x, ps.y, true)!
            tile.node.name = 'tile-' + ps.x + '-' + ps.y
            const sdNode = instantiate(this.buildingShadowPref)!
            const tv = tile.node.position
            sdNode.setPosition(new Vec3(tv.x + this.tiledMap.getTileSize().width / 2, tv.y + this.tiledMap.getTileSize().height / 2))
            sdNode.getComponent(Sprite)!.color = this._shadowColor(cellId)
            this.shadowPool.push(new Shadow(sdNode, cellId))
            this.buildingShadowsBox.addChild(sdNode)
        }
    }

    _occupiedCell(cellIds: bigint[]) {
        for (const cellId of cellIds) {
            const ps = calcTilePositionByCellId(cellId);
            const tile = this.baseLayer.getTiledTileAt(ps.x, ps.y, true)!
            const sdNode = this.shadowPool.shift()!
            const tv = tile.node.position
            sdNode.node.setPosition(new Vec3(tv.x + this.tiledMap.getTileSize().width / 2, tv.y + this.tiledMap.getTileSize().height / 2))
            sdNode.node.getComponent(Sprite)!.color = this._shadowColor(cellId)
            sdNode.cellId = cellId
            this.shadowPool.push(sdNode)
        }
    }

    _shadowColor(cellId: bigint): Color {
        if (this.occupiedCellIds.has(cellId)) return new Color(255, 0, 0, 255)
        else return new Color(0, 255, 0, 255)
    }

    _scrollMovePos(targetPos: Vec3): void {
        this.scrollView.scrollToOffset(Vec2.ZERO, 0)
        const wOffset = this.node.getComponent(UITransform)!.width / 2
        const hOffset = this.node.getComponent(UITransform)!.height / 2
        const tg = new Vec2(targetPos.x - wOffset, (-targetPos.y) - hOffset)
        this.scrollView.scrollToOffset(tg, 0.2)
    }

    _scrollMoveTiled(x: number, y: number): void {
        // this.scrollView.scrollToOffset(Vec2.ZERO, 0)
        // const target = this.baseLayer.getTiledTileAt(x, y, true)
        const targetPos = this.baseLayer.getPositionAt(x, y)
        console.log('scroll view content position', this.scrollView.content!.position)
        console.log('title map position', this.tiledMap.node!.position)
        console.log('target tiled position', targetPos)
        console.log('scroll view content size', this.scrollView.content!.getComponent(UITransform)!.width, this.scrollView.content!.getComponent(UITransform)!.height)
        const tileUI = this.baseLayer.getComponent(UITransform)!
        console.log('tile UI size', tileUI.width, tileUI.height)
        if (!targetPos) return
        const wOffset = this.node.getComponent(UITransform)!.width / 2
        const hOffset = this.node.getComponent(UITransform)!.height / 2
        console.log('wOffset', wOffset, 'hOffset', hOffset)
        // const tg = new Vec2(targetPos.x - wOffset + this.tiledMap.node.x - this.tiledMap.getTileSize().width / 2, (tileUI.height - targetPos.y) - hOffset)
        const tg = new Vec2(targetPos.x + this.cityBase.x - wOffset, -targetPos.y - this.cityBase.y - hOffset)
        console.log('score offset ', tg)
        this.scrollView.scrollToOffset(tg, 0.2)
    }

    protected onDestroy() {
        this.offEvent()
    }

}